#ifndef SPHERECUH
#define SPHERECUH

#include "hitable.cuh"
#include"material.cuh"
class sphere: public hitable  {
    public:
        __duel__ sphere() {mat_ptr=nullptr;center=vec3(0,0,0);radius=0.0f;}
        __duel__ sphere(vec3 cen, float r, material *m) : center(cen), radius(r), mat_ptr(m)  {};
        __device__ virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
        __duel__ virtual bool bounding_box(float t0, float t1, aabb& output_box) const;
        vec3 center;
        float radius;
        material *mat_ptr;
        __duel__ ~sphere() 
        {
            THISNULL(mat_ptr);
            
            
        }
        __duel__ float get_radius(){return this->radius;}
};
__duel__  bool sphere::bounding_box(float t0, float t1, aabb& output_box) const
{
    
    output_box= aabb(
        center-vec3(radius,radius,radius),
        center+vec3(radius,radius,radius)
    );
    //output_box.DisInfo();
    return true;
};
__device__ bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
    
    vec3 oc = r.origin() - center;
    float a = dot(r.direction(), r.direction());
    float b = dot(oc, r.direction());
    float c = dot(oc, oc) - radius*radius;
    float discriminant = b*b - a*c;
    if (discriminant > 0) {
        float temp = (-b - sqrt(discriminant))/a;
        if (temp < t_max && temp > t_min) {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            vec3 outward_normal = (rec.p - center) / radius;
            rec.set_face_normal(r, outward_normal);
            //rec.normal = (rec.p - center) / radius;
            get_sphere_uv((rec.p - center)/radius,rec.u,rec.v);
            rec.mat_ptr = mat_ptr;
            return true;
        }
        temp = (-b + sqrt(discriminant)) / a;
        if (temp < t_max && temp > t_min) {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            vec3 outward_normal = (rec.p - center) / radius;
            rec.set_face_normal(r, outward_normal);
            //rec.normal = (rec.p - center) / radius;
            rec.mat_ptr = mat_ptr;
            return true;
        }
    }
    return false;
}


class moving_sphere : public hitable {
    public:
        __duel__ moving_sphere() {}
        __duel__ moving_sphere(
            vec3 cen0, vec3 cen1, float t0, float t1, float r, material* m)
            : center0(cen0), center1(cen1), time0(t0), time1(t1), radius(r), mat_ptr(m)
        {};

        __device__ virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
        __duel__ virtual bool bounding_box(float t0, float t1, aabb& output_box) const;
        __duel__ vec3 center(float time) const;
        __duel__ ~moving_sphere() {THISNULL(mat_ptr);}

    public:
        vec3 center0,center1;
        float time0,time1;
        float radius;
        material *mat_ptr;
};
__duel__  bool moving_sphere::bounding_box(float t0, float t1, aabb& output_box) const
{
    
    aabb box0(
        center(t0) - vec3(radius, radius, radius),
        center(t0) + vec3(radius, radius, radius));
    aabb box1(
        center(t1) - vec3(radius, radius, radius),
        center(t1) + vec3(radius, radius, radius));
    output_box = surrounding_box(box0, box1);
    return true;
};
__duel__ vec3 moving_sphere::center(float time) const{
    return center0 + ((time - time0) / (time1 - time0))*(center1 - center0);
}

__device__ bool moving_sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
    vec3 nowcenter=center(r.time());
    vec3 oc = r.origin() - nowcenter;
    float a = dot(r.direction(), r.direction());
    float b = dot(oc, r.direction());
    float c = dot(oc, oc) - radius*radius;
    float discriminant = b*b - a*c;
    if (discriminant > 0) {
        float temp = (-b - sqrt(discriminant))/a;
        if (temp < t_max && temp > t_min) {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            vec3 outward_normal = (rec.p - nowcenter) / radius;
            rec.set_face_normal(r, outward_normal);
            //rec.normal = (rec.p - center) / radius;
            rec.mat_ptr = mat_ptr;
            return true;
        }
        temp = (-b + sqrt(discriminant)) / a;
        if (temp < t_max && temp > t_min) {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            vec3 outward_normal = (rec.p - nowcenter) / radius;
            rec.set_face_normal(r, outward_normal);
            //rec.normal = (rec.p - center) / radius;
            rec.mat_ptr = mat_ptr;
            return true;
        }
    }
    return false;
}



#endif
